package org.eden.karb.device.audio.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eden.karb.device.audio.AudioConfiguration;
import org.eden.karb.device.audio.AudioDevice;
import org.eden.karb.exception.AudioConfigurationNotSetException;
import org.eden.karb.exception.KarbException;

import javax.sound.sampled.TargetDataLine;
import java.util.UUID;

/**
 * Abstract audio device implementation.
 *
 * @author trsimoes
 */
public abstract class AbstractAudioDeviceImpl extends Thread implements AudioDevice {

    protected Log log = LogFactory.getLog(AbstractAudioDeviceImpl.class);

    protected AudioConfiguration configuration;

    private UUID key;

    protected long lastEvent = -1;

    public AbstractAudioDeviceImpl() {
        this.key = UUID.randomUUID();
    }

    protected boolean running;

    @Override
    public void run() {

        running = true;

        byte audioBuffer[] = new byte[configuration.getBufferSize()];

        TargetDataLine targetRecordLine = configuration.getTargetRecordLine();

        while (running) {

            int numberOfBytesRead = targetRecordLine.read(audioBuffer, 0, audioBuffer.length);
            if (numberOfBytesRead > 0) {
                execute(audioBuffer, numberOfBytesRead);
            }
        }

        log.debug("Shutting down audio device thread [" + getClass().getSimpleName() + "]");

        try {
            closeResources();
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("An error occurred while closing resources.", e);
            }
        }

    }

    protected abstract void execute(byte audioInBuffer[], int numberOfBytesRead);

    protected abstract void closeResources() throws Exception;

    public void startDevice() throws KarbException {

        if (this.configuration == null) {
            throw new AudioConfigurationNotSetException();
        }

        log.debug("Starting audio device [" + getClass().getSimpleName() + "]");
        this.lastEvent = System.currentTimeMillis();
        start();
    }

    public void stopDevice() {
        log.debug("Stopping audio device [" + getClass().getSimpleName() + "]");
        running = false;
    }

    public UUID getKey() {
        return key;
    }

    public void setKey(UUID key) {
        this.key = key;
    }

    public AudioConfiguration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(AudioConfiguration configuration) {
        this.configuration = configuration;
    }
}
